package ch.uzh.ifi.seal.ssqa.storage;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.List;
import java.util.Properties;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.teneo.PersistenceOptions;
import org.eclipse.emf.teneo.hibernate.HbDataStore;
import org.eclipse.emf.teneo.hibernate.HbHelper;
import org.eclipse.jface.preference.IPreferenceStore;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.MySQL5Dialect;

import ch.uzh.ifi.seal.ssqa.SsqaPlugin;
import ch.uzh.ifi.seal.ssqa.model.Issue;
import ch.uzh.ifi.seal.ssqa.model.ModelPackage;
import ch.uzh.ifi.seal.ssqa.model.Person;
import ch.uzh.ifi.seal.ssqa.preferences.PreferenceConstants;

/**
 * 
 * @author mkn change by yi. Fixed the bugs in create new database schema.
 * 
 */
public class TeneoHibernateStorage implements IStorage {

	private String storageIdentifier;
	private HbDataStore dataStore = null;

	public TeneoHibernateStorage(String storageIdentifier) {
		this.init(storageIdentifier);
	}

	public TeneoHibernateStorage() {
		IPreferenceStore store = SsqaPlugin.getDefault().getPreferenceStore();
		this.init(store.getString(PreferenceConstants.STORAGE_DRIVER));
	}

	private void init(String storageIdentifier) {
		if (storageIdentifier == null) {
			this.storageIdentifier = "Default_TeneoHibernateStorage";
		} else {
			this.storageIdentifier = storageIdentifier;
		}
		this.loadStore();
		this.loadDefaultProperties();
	}

	private void loadStore() {
		this.dataStore = HbHelper.INSTANCE
				.createRegisterDataStore(this.storageIdentifier);
		this.dataStore.setEPackages(new EPackage[] { ModelPackage.eINSTANCE });
	}

	private void loadDefaultProperties() {
		Properties props = new Properties();
		IPreferenceStore store = SsqaPlugin.getDefault().getPreferenceStore();
		props.setProperty(Environment.DRIVER,
				store.getString(PreferenceConstants.STORAGE_DRIVER));
		props.setProperty(Environment.USER,
				store.getString(PreferenceConstants.STORAGE_USER));
		props.setProperty(
				Environment.URL,
				store.getString(PreferenceConstants.STORAGE_URI) + "/"
						+ store.getString(PreferenceConstants.STORAGE_DB));
		props.setProperty(Environment.PASS,
				store.getString(PreferenceConstants.STORAGE_PW));
		props.setProperty(Environment.DIALECT, MySQL5Dialect.class.getName());
		props.setProperty(PersistenceOptions.CASCADE_POLICY_ON_CONTAINMENT,
				"ALL");
		props.setProperty(PersistenceOptions.CASCADE_POLICY_ON_NON_CONTAINMENT,
				"MERGE,PERSIST,REFRESH");
		props.setProperty("teneo.mapping.always_version", "true");
		this.dataStore.setDataStoreProperties(props);
	}

	public void initDataStore() {
		if (!this.dataStore.isInitialized()) {
			this.dataStore.initialize();
		}
	}

	public void closeDataStore() {
		if (this.dataStore.isInitialized()) {
			this.dataStore.close();
		}
	}

	public boolean isInitialized() {
		return this.dataStore.isInitialized();
	}

	public void saveEntity(EObject object) {
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		session.save(object);
		session.getTransaction().commit();
		session.close();
	}

/*	
 * Previous Version of Marc
 * public void saveOrUpdateEntity(Collection<? extends EObject> list) {
		for (EObject object : list) {
			this.saveOrUpdateEntity(object);
		}
	}*/
	
	public void saveOrUpdateEntity(Collection<? extends EObject> list) {
		Session session  = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();		
		for (EObject object : list) {
			session.saveOrUpdate(object);
		}
		session.getTransaction().commit();
		session.close();
	}	

	public void saveOrUpdateEntity(EObject object) {
		if (object instanceof Person) {
			System.out.println(((Person) object).getName());
		} else if (object instanceof Issue) {
			System.out.println(((Issue) object).getId()
					+ " headline: " +  ((Issue) object).getHeadline());
			//return;
		}
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		session.saveOrUpdate(object);
		//session.merge(object);
		session.getTransaction().commit();
		session.close();
	}

	public void deleteEntity(EObject object) {
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		session.delete(object);
		session.getTransaction().commit();
		session.close();
	}

	public StorageResult executeListQuery(String queryString) {
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		Query query = session.createQuery(queryString);
		StorageResult result = new StorageResult(session,
				(List<?>) query.list());
		return result;
	}

	public StorageResult executeUniqueQuery(String queryString) {
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		Query query = session.createQuery(queryString);
		StorageResult result = new StorageResult(session, query.uniqueResult());
		return result;
	}

	public StorageResult executeListSQLQuery(String queryString) {
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		SQLQuery query = session.createSQLQuery(queryString);
		StorageResult result = new StorageResult(session,
				(List<?>) query.list());
		return result;
	}

	public StorageResult executeUniqueSQLQuery(String queryString) {
		Session session = this.dataStore.getSessionFactory().openSession();
		session.beginTransaction();
		SQLQuery query = session.createSQLQuery(queryString);
		StorageResult result = new StorageResult(session, query.uniqueResult());
		return result;
	}

	public void createNewModelDB() {
		IPreferenceStore store = SsqaPlugin.getDefault().getPreferenceStore();
		try {
			Class.forName("com.mysql.jdbc.Driver");
			Connection con = DriverManager.getConnection(
					store.getString(PreferenceConstants.STORAGE_URI),
					store.getString(PreferenceConstants.STORAGE_USER),
					store.getString(PreferenceConstants.STORAGE_PW));
			DatabaseMetaData meta = con.getMetaData();
			ResultSet rs = meta.getCatalogs();
			boolean dropDbFirst = false;
			while (rs.next()) {
				if (rs.getString("TABLE_CAT").equals(
						store.getString(PreferenceConstants.STORAGE_DB))) {
					dropDbFirst = true;
					break;
				}
			}
			Statement statement;
			if (dropDbFirst) {
				statement = con.createStatement();
				statement.execute("DROP DATABASE ssqa");
			}
			statement = con.createStatement();
			statement.execute("CREATE DATABASE "
					+ store.getString(PreferenceConstants.STORAGE_DB));
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
